﻿using AspectCore.DynamicProxy;
using RAP.Framework.Libary.DataSource.Model;
using RAP.Framework.Libary.DataSource.Model.Interceptor;
using RAP.Framework.Libary.Utils;
using System.Collections;
using RAP.Framework.Libary.DataSource;
using System;
using RAP.Framework.Libary.SqlDataService.Service;
using static RAP.Framework.Libary.DataSource.ServiceUtils;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Reflection;
using RAP.Framework.Libary.DataSource.Context;

namespace RAP.Framework.Libary.SqlDataService.Interceptor
{
  /// <summary>
  /// 基于SQL查询服务的数据模型懒加载拦截器
  /// </summary>
  public class SqlLazyLoadInterceptor<TSqlDataService> : AbstractLazyLoadInterceptor where TSqlDataService : ISqlDataService
  {
    private static Regex _methodNameReplacer = new Regex("^(get|load|query)", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase);

    /// <summary>
    /// 默认的数据模型参数字段名称：@model.*
    /// </summary>
    private const string DEFAUL_DATA_MODEL_FIELD = "model";

    /// <summary>
    /// 获取或设置数据模型参数字段名称
    /// </summary>
    public string DataModelField { get; set; }

    /// <summary>
    /// 执行数据查询的对象，必须是实现了TSqlDataService接口的对象
    /// </summary>
    protected TSqlDataService SqlDataService { get; set; }

    protected IServiceProvider ServiceProvider { get; set; }

    public SqlLazyLoadInterceptor(IServiceProvider serviceProvider) : this(serviceProvider, DEFAUL_DATA_MODEL_FIELD) { }

    public SqlLazyLoadInterceptor(IServiceProvider serviceProvider, string dataModelField = DEFAUL_DATA_MODEL_FIELD)
    {
      ServiceProvider = serviceProvider;
      SqlDataService = (TSqlDataService)serviceProvider.GetService(typeof(TSqlDataService));
      DataModelField = dataModelField;
    }

    /// <summary>
    /// 从ServiceProvider获取IQueryParams的实例
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    protected virtual IQueryParams CreateQueryParams(IDictionary param = null)
    {
      var queryParams = (IQueryParams)ServiceProvider.GetService(typeof(IQueryParams));
      queryParams.CopyFrom(param);
      return queryParams;
    }

    protected override async Task<object> QueryData(
      IDataModel sourceModelObject,
      AspectContext context,
      InterceptorContext interceptorContext
      )
    {
      var modelContext = sourceModelObject.GetContext();
      var nameSpace = modelContext.TemplateId;//使用数据模型被查询出来时使用的模板Id作为当前模板的namespace
      var methodName = interceptorContext.MemberInfo.Name;
      var attr = interceptorContext.LazyLoadAttr;
      var propertyInfo = interceptorContext.MemberInfo as PropertyInfo;
      if (attr.Cache && propertyInfo != null)
      {
        //如果是属性，则判断属性已有值，如果已有值则直接返回
        var value = sourceModelObject.GetCachedLazyloadValue(methodName);
        if (value != null)
        {
          return value;
        }
      }
      methodName = _methodNameReplacer.Replace(methodName, string.Empty);//去除函数名称前缀：get、load
      var templateId = attr.TemplateId.IsNullOrEmptyValue(methodName);
      var resultAlias = attr.ResultAlias.IsNullOrEmptyValue(methodName);
      if (templateId.IndexOf('.') < 0)
      {
        //如果模板名称不是完全的名称，则将数据模型查询的模板名称作为Namespace
        templateId = $"{nameSpace}.{templateId}";
      }

      SqlDataService.NameSpace = nameSpace;
      var queryParams = CreateQueryParams(new Hashtable
      {
        {
          DataModelField, sourceModelObject
        },
        {
          DATA_SOURCE_FIELD, modelContext.DataSourceId
        }
      });
      var result = await SqlDataService.ExecuteNoCache(queryParams, templateName: templateId);
      if (result.HasError)
      {
        throw new Exception(result.Error.ErrorMessage);
      }
      object resultValue = null;
      var resultData = result.Data as IDictionary;

      if (context.ProxyMethod.ReturnType.GetInterface(nameof(IDataModel)) != null)
      {
        //如果被拦截属性或方法返回的类型是IDataModel接口类型，则返回查询结果的第一个数据
        resultValue = resultData.ToList(resultAlias).First<object>();
        //resultValue = list.IsNotNullOrEmpty() ? list.FirstItem() : null;
      }
      else
      {
        resultValue = resultData[resultAlias];
      }
      if (attr.Cache && propertyInfo != null)
      {
        //缓存懒加载的数据
        sourceModelObject.CacheLazyloadValue(methodName, resultValue);
      }
      return resultValue;
    }
  }
}
